package com.cdg.polymeric.core.ext.mybatis;


import com.cdg.polymeric.DefaultEnvironment;
import com.cdg.polymeric.Collectors;
import com.cdg.polymeric.Environment;
import com.cdg.polymeric.config.AnnotationSetting;
import com.cdg.polymeric.core.CollectorsHandle;
import com.cdg.polymeric.core.ext.collectors.PolymericClassCollector;
import com.cdg.polymeric.core.ext.excutor.AbstactFeignTemplateExcutor;
import com.cdg.polymeric.core.ext.feign.DefaultFeignExcutor;
import com.cdg.polymeric.core.ext.collectors.PolymericFieldCollector;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.resultset.DefaultResultSetHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.Reflector;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicReference;

@Intercepts(@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,
                RowBounds.class, ResultHandler.class}))
public class PolymericMybatisPlugin implements Interceptor{

    /**
     * polymeric 额外的配置属性
     */
   private static volatile  Properties properties;



   public ConcurrentLinkedQueue<AbstactFeignTemplateExcutor> excutors = new ConcurrentLinkedQueue<>();



    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        DefaultResultSetHandler resultSetHandler = null;
        MappedStatement mappedStatement = null;
        // 获取被代理的对象
        Object  target = invocation.getTarget();
        // 获取被代理的方法
        Method targetMethod = invocation.getMethod();
        Object[] args = invocation.getArgs();
        mappedStatement = (MappedStatement) args[0];
        resultSetHandler = (DefaultResultSetHandler) args[3];
        List<ResultMap> resultMaps =  mappedStatement.getResultMaps();
        Configuration configuration = mappedStatement.getConfiguration();
        Reflector reflector = configuration.getReflectorFactory().findForClass(resultMaps.get(0).getType());
        // 定义收集器
        PolymericClassCollector polymericClassCollector = new PolymericClassCollector(reflector);
        PolymericFieldCollector polymericFieldCollector = new PolymericFieldCollector(reflector);
        ArrayList<Collectors> collectors = new ArrayList();
        collectors.add(polymericClassCollector);
        collectors.add(polymericFieldCollector);
        CollectorsHandle collectorsHandle =  new CollectorsHandle();
        CopyOnWriteArrayList annotationSettings = collectorsHandle.collectorProcess(collectors).getAnnotationSettings();
        AtomicReference<Object> result = new AtomicReference<>(invocation.proceed());

        if (annotationSettings.size() != 0){
            // 执行远程查询
            annotationSettings.forEach(a->{
                AnnotationSetting annotationSetting = (AnnotationSetting) a;
                AbstactFeignTemplateExcutor abstactTemplateExcutor = new DefaultFeignExcutor(annotationSetting,properties);
                Environment environment = new DefaultEnvironment(abstactTemplateExcutor,annotationSetting,reflector);
                annotationSetting.setEnvironment(environment);
                a = annotationSetting;
                excutors.add(abstactTemplateExcutor);
            });
            // 执行队列
            excutors.stream().forEach(e->{
                e.setProperties(properties);
                e.setMybatisQueryResult(result.get());
                e.setReflector(reflector);
                result.set(e.join());
                excutors.remove(e);
            });


        }

        return result.get();

    }



    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target,this);
    }

    @Override
    public void setProperties(Properties properties) {
        PolymericMybatisPlugin.properties = properties;
    }

}
